﻿#region Licence
/*
 * Microsoft Public License (Ms-PL)

This license governs use of the accompanying software. If you use the software, you accept this license. 
 * If you do not accept the license, do not use the software.

1. Definitions

The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.

A "contribution" is the original software, or any additions or changes to the software.

A "contributor" is any person that distributes its contribution under this license.

"Licensed patents" are a contributor's patent claims that read directly on its contribution.

2. Grant of Rights

(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
 * each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, 
 * prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.

(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
 * each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, 
 * use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the 
 * contribution in the software.

3. Conditions and Limitations

(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.

(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent 
 * license from such contributor to the software ends automatically.

(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that 
 * are present in the software.

(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete 
 * copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, 
 * you may only do so under a license that complies with this license.

(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or 
 * conditions. You may have additional consumer rights under your local laws which this license cannot change. 
 * To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, 
 * fitness for a particular purpose and non-infringement. 
 */
#endregion

#region Usings
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Threading;
using XMLSpriteSheets.Utilities;
#endregion

#region Namespace
namespace XMLSpriteSheets
{
    public enum XMLType
    {
        SpriteSheetPacker,
        TexturePacker
    }
    #region Class
    public partial class mainFrm : Form
    {
        #region Fields and Properties
        public string xmlFile { get; private set; }

        public string xmlSheet { get; private set; }

        public Dictionary<string, Rectangle> mappedSprites = new Dictionary<string, Rectangle>();

        /// <summary>
        /// Our final animation list by framname and image
        /// </summary>
        private Dictionary<string, Image> animationList = new Dictionary<string, Image>();
        public Dictionary<string, Image> AnimationList
        {
            get { return animationList; }
            set { animationList = value; }
        }

        List<string> frameListNames = new List<string>();
        List<Image> frameListImages = new List<Image>();

        

        private SpriteSheetFileLoader loader { get; set; }

        public Image SpriteSheet { get; private set; }

        public Thread loading { get; set; }

        public Utils Util = new Utils();

        public ImageList aniList = new ImageList();

        public int TimerTick = 0;

        public decimal filesLoaded { get; set; }
        public decimal filesToLoad { get; set; }
        public decimal percent { get; set; }

        public XMLType xmltype { get; set; }

        XMLReader reader { get; set; }

        #endregion

        #region Constructor
        public mainFrm()
        {
            InitializeComponent();
            reader = new XMLReader(this);
        }

        #endregion

        #region Form Load

        private void mainFrm_Load(object sender, EventArgs e)
        {
            // Set up some vars for loading, specifically our file filters
            //openSheet and openXML
            // Xml first, its shorter
            openXML.DefaultExt = "xml";
            openXML.Filter = "Xml Files (*.xml)|*.xml|Text Files (*.txt*)|*.txt*";
            openXML.Title = "Select a XML file with the SpriteSheet format.";

            // Now set up the graphic files we support
            openSheet.DefaultExt = "png";
            openSheet.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg|BMP Files (*.bmp)|*.bmp";
            openSheet.Title = "Select the SpriteSheet that this XML File maps.";


        }
        #endregion

        #region Load Spritesheet
        private void btn_LoadSheet_Click(object sender, EventArgs e)
        {
            xmlSheet = "";
            xmlFile = "";
            // If the timer is running, turn it off
            if (animateTimer.Enabled)
            {
                // Reset the animate timer so we always restart at frame 1, if this is aborted
                btn_StopAnimation_Click(sender, e);
                TimerTick = 0;
                aniList = null;
                percent = 0;
                progressBar.Value = 0;
                xmlSheet = null;
                if (mappedSprites != null) { mappedSprites.Clear(); }
                if (AnimationList != null) { AnimationList.Clear(); }
                if (imageBoxAni.Image != null) { imageBoxAni.Image = null; }
               
                if (frameListImages != null) { frameListImages.Clear(); }
                if (frameListNames != null) { frameListNames.Clear(); }
                
                //this.BeginInvoke(new MethodInvoker(delegate { progressBar.Value = 0; }));
                // reset our progressbar
                // Turn our play button back on so we can play again, if this new load is aborted.
                //this.BeginInvoke(new MethodInvoker(delegate { btn_PlayAnimation.Enabled = true; }));
            }
            else if (!animateTimer.Enabled)
            {
                // New Load Clear everything
                TimerTick = 0;
                aniList = null;
                percent = 0;
                progressBar.Value = 0;
                if (mappedSprites != null) { mappedSprites.Clear(); }
                if (AnimationList != null) { AnimationList.Clear(); }
                if (imageBoxAni.Image != null) { imageBoxAni.Image = null; }

                if (frameListImages != null) { frameListImages.Clear(); }
                if (frameListNames != null) { frameListNames.Clear(); }
            }

            string path;
            path = System.IO.Path.GetDirectoryName(
               System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
            xmlFile = "";
            xmlSheet = "";
            openSheet.FileName = null;
            if (openXML.ShowDialog() == DialogResult.OK)
            {
                // we picked a xml file, load it into memory and pick the sprite sheet
                xmlFile = openXML.FileName;


                if (openSheet.ShowDialog() == DialogResult.OK)
                {
                    xmlSheet = openSheet.FileName;
                    //SpriteSheet = Image.FromFile(openSheet.FileName);
                }
                else
                {
                    // Reset xml name
                    openXML.FileName = "";
                    //Give an error
                    DialogResult retry = MessageBox.Show("You must select both a XML file, and its graphical spritesheet.",
                        "Error !!, No SpriteSheet Selected.",
                        MessageBoxButtons.RetryCancel, MessageBoxIcon.Exclamation);
                    if (retry == DialogResult.Retry)
                    {
                        openXML.FileName = "";
                        openSheet.FileName = "";
                        btn_LoadSheet_Click(sender, e);
                    }
                    // Opensheet was not a ok result, so we cant load, give an error and return
                    MessageBox.Show(
                        "Unable to load File: " + xmlSheet + " aborting attempt.",
                        "File Load Error!!", MessageBoxButtons.OK,
                         MessageBoxIcon.Stop);
                }
                // All was good
                if (openSheet.FileName.Length != 0 || openXML.FileName.Length != 0)
                {
                    BackgroundFileLoading();
                        

                }
            }
            // Wasnt ok do do anything
        }
        #endregion





        #region Count Frames
        public void CountFrames()
        {
            if (AnimationList.Count == 0)
            {
                rtf_Console.AppendText("Error List not loaded.\n");
                return;
            }
            StringBuilder build = new StringBuilder();
            build.AppendLine("\n\nFrames in Spritesheet: " + AnimationList.Count);

            //Load furst 1

            // Seperate these, but frameListNames[0] and frameListImages[0] will be the same info
            foreach (KeyValuePair<string, Image> o in AnimationList)
            {
                frameListNames.Add(o.Key);
                frameListImages.Add(o.Value);
            }
            // Remove the progress bar
            if (frameListNames[0] == null || frameListImages[0] == null) { return; }
            this.Invoke((MethodInvoker)delegate
            {
                Text = frameListNames[0] + " Image size: " + frameListImages[0].PhysicalDimension;
            });

            imageBoxAni.Invoke((MethodInvoker)delegate
            {
                imageBoxAni.Image = frameListImages[0];
            });

            if (this.rtf_Console.InvokeRequired)
            {
                rtf_Console.Invoke((MethodInvoker)delegate
                {
                    rtf_Console.AppendText(build.ToString() + "\n");
                });
            }
            
            // Enable all buttons
            this.BeginInvoke(new MethodInvoker(delegate { btn_Viewer.Enabled = true; }));

            this.BeginInvoke(new MethodInvoker(delegate { btn_PlayAnimation.Enabled = true; }));

            this.BeginInvoke(new MethodInvoker(delegate { btn_StopAnimation.Enabled = true; }));
            // Clear the Animation List
            AnimationList.Clear();
        }
        #endregion

        #region Animation
        /// <summary>
        /// 
        /// </summary>
        /// <param name="szPath"></param>
        /// <returns></returns>
        private string getFileName(string szPath)
        {
            string[] szTokens = szPath.Split('.');
            return szTokens[szTokens.Length - 1];
        }

        /// <summary>
        /// Handles the animation by frame, the timer does it for us :)
        /// </summary>
        /// <param name="speed"></param>
        public void Animate(int speed)
        {
            // Turn the play button off while animating until the stop button is pressed
            this.BeginInvoke(new MethodInvoker(delegate { btn_PlayAnimation.Enabled = false; }));

            progressBar.ForeColor = Color.MediumPurple;
            filesLoaded = 0;
            animateTimer.Tick += new EventHandler(animateTimer_Tick);
            animateTimer.Interval = speed;
            animateTimer.Start();


        }


        public void animateTimer_Tick(object sender, EventArgs e)
        {
            filesToLoad = frameListImages.Count; // How many images we have
            // Each time we come here, we are showing 1 file before we stop, so add that file now
            //if (filesLoaded >= filesToLoad) { filesLoaded = 0; }



            // If timertick is 0 then this is our first time.

            // how many frames ?
            if (TimerTick == filesToLoad)
            {
                // Start Over 
                TimerTick = 0;
                progressBar.Value = 0;
                progressBar.Refresh();
                filesLoaded = 0;
                percent = 0;
            }
            else
            {
                //MessageBox.Show("Tick " + TimerTick);
                imageBoxAni.Invoke((MethodInvoker)delegate
                {
                    imageBoxAni.Image = frameListImages[TimerTick];
                });

                this.Invoke((MethodInvoker)delegate
                {
                    Text = frameListNames[TimerTick] + " Image size: " + frameListImages[TimerTick].PhysicalDimension;
                });

            }
            percent = Math.Round((filesLoaded / filesToLoad) * 100);

            progressBar.Value = (int)percent;

            filesLoaded++;
            TimerTick++;
            //imageBoxAni.Image = 
        }
        #endregion

        #region Buttons

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Viewer_Click(object sender, EventArgs e)
        {
            ViewSpriteSheet view = new ViewSpriteSheet(this, SpriteSheet);
            view.Show();
        }

        private void btn_PlayAnimation_Click(object sender, EventArgs e)
        {
            // Reset timer tick or boom !!!
            TimerTick = 0;
            // Our percentage as well
            percent = 0;
            progressBar.Value = 0;
            progressBar.Refresh();

            try
            {
                int speed;
                int.TryParse(speedBox.Text, out speed);
                Animate(speed);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "ERROR ! Unable to use listed speed, it must be in whole milliseconds.",
                    MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        private void btn_StopAnimation_Click(object sender, EventArgs e)
        {
            animateTimer.Stop();
            // Turn the play button back on
            this.BeginInvoke(new MethodInvoker(delegate { btn_PlayAnimation.Enabled = true; }));
            // Redundently reset the timer so we always restart at frame 0
            TimerTick = 0;
            percent = 0;
            progressBar.Value = 0;
        }


        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            StringBuilder about = new StringBuilder();
            string[] info = {
            "Written by me for you, ImageBox control supplied by",
            "Cyotek ImageBox Copyright (c) 2010 Cyotek.",
            "All Rights Reserved. http://cyotek.com",
            "",
            "Sample Sprite's created by",
            "Emergence Copyright (c) 2011 Emergence",
            "",
            "Sprite Sheets and xml files made with",
            "http://spritesheetpacker.codeplex.com/"
                            };

            AboutXMLAnimator box = new AboutXMLAnimator();
            box.textBoxDescription.Lines = info;
            //box.li = about.ToString();
            box.Show();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
            this.Dispose();
        }

        private void loadXMLSpritesheetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            btn_LoadSheet_Click(sender, e);
        }

        private void viewFullSpritesheetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (xmlSheet == null)
            {
                MessageBox.Show(
                    "You need to load a spritesheet in first.",
                    "No Sprite Sheet Loaded.",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Stop);

            }
            else { btn_Viewer_Click(sender, e); }
        }

        private void btn_ChangeBackground_Click(object sender, EventArgs e)
        {
            if (colorDialog.ShowDialog() == DialogResult.OK)
            {
                // Turn on image background grid wont show it
                imageBoxAni.GridDisplayMode = ImageBoxGridDisplayMode.None;
                // Get the color the user chose
                imageBoxAni.BackColor = colorDialog.Color;
            }

        }

        private void selectBackgroundColorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (colorDialog.ShowDialog() == DialogResult.OK)
            {
                // Turn on image background grid wont show it
                imageBoxAni.GridDisplayMode = ImageBoxGridDisplayMode.None;
                // Get the color the user chose
                imageBoxAni.BackColor = colorDialog.Color;
            }
        }

        private void resetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            imageBoxAni.GridDisplayMode = ImageBoxGridDisplayMode.Client;
        }

        private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Are you sure you wish to exit this program ?",
                "Exit Program ?",
            MessageBoxButtons.YesNoCancel,
            MessageBoxIcon.Question);
            if (result == DialogResult.Yes)
            {
                this.Close();
                this.Dispose();
            }
        }

        private void viewSpriteSheetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            viewFullSpritesheetToolStripMenuItem_Click(sender, e);
        }

        private void loadXMLSpriteSheetToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            btn_LoadSheet_Click(sender, e);
        }



        public void btn_LoadSheet_MouseEnter(object sender, EventArgs e)
        {
            var bmp = new Bitmap(XMLSpriteSheets.Properties.Resources.cooltext687581605MouseOver);


            btn_LoadSheet.BackgroundImage = bmp;
        }

        public void btn_LoadSheet_MouseLeave(object sender, EventArgs e)
        {
            var bmp = new Bitmap(XMLSpriteSheets.Properties.Resources.cooltext687581605);


            btn_LoadSheet.BackgroundImage = bmp;
        }
        public void btn_Viewer_MouseEnter(object sender, EventArgs e)
        {
            var bmp = new Bitmap(XMLSpriteSheets.Properties.Resources.viewxmlMouseOver);


            btn_Viewer.BackgroundImage = bmp;
        }

        public void btn_Viewer_MouseLeave(object sender, EventArgs e)
        {
            var bmp = new Bitmap(XMLSpriteSheets.Properties.Resources.viewxml);


            btn_Viewer.BackgroundImage = bmp;
        }


        private void helpTopicsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start(Application.StartupPath + "\\XML_Sprite_Sheet_Animator_Help.chm");
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    ex.ToString() + "\n" + Application.StartupPath + "\\XML_Sprite_Sheet_Animator_Help.chm",
                    "Error Loading Help file",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Stop);
            }
        }
        #endregion

        public void BackgroundFileLoading()
        {
            progressBar.Visible = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {


                //rtf_Console.AppendText("Loading Completed.\n");
            
           
                    btn_LoadSheet.Enabled = true;
                


                btn_Viewer.Enabled = true;
        }

        void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            if (this.btn_LoadSheet.InvokeRequired)
            {
                btn_LoadSheet.Invoke((MethodInvoker)delegate
                {
                    btn_LoadSheet.Enabled = false;
                });
            }

            if (this.btn_Viewer.InvokeRequired)
            {
                btn_Viewer.Invoke((MethodInvoker)delegate
                {
                    btn_Viewer.Enabled = false;
                });
            }
            BackgroundWorker worker = sender as BackgroundWorker;

            // Xml file looks good, image looks good, load them and break them apart
            // Move this to the reader, and make 1 for SSP and one for TP
            if (AnimationList == null) { return; }
            AnimationList.Clear();
            if (this.rtf_Console.InvokeRequired)
            {
                this.rtf_Console.Invoke((MethodInvoker)delegate
                {
                    rtf_Console.AppendText("Trying to Load as Sprite Packer.\n");
                });

            }


 

            AnimationList = reader.ReadSP(xmlFile, Image.FromFile(xmlSheet));
            
            // AnimationList = reader.ReadSP(xmlFile, Image.FromFile(xmlSheet)); // Try to load as spritepacker
            if (AnimationList == null) { return; }
            if (AnimationList.Count != 0)
            {
                // Good load 
                if (this.rtf_Console.InvokeRequired)
                {
                    this.rtf_Console.Invoke((MethodInvoker)delegate
                    {
                        rtf_Console.AppendText("Attempting to load as Sprite Packer....\n");
                    });

                }
                
            }
            else
            {
                if (this.rtf_Console.InvokeRequired)
                {
                    this.rtf_Console.Invoke((MethodInvoker)delegate
                    {
                        rtf_Console.AppendText("Failed loading as sprite Packer, trying Texture Packer.\n");
                    });

                }
                

                AnimationList = reader.ReadTP(xmlFile, Image.FromFile(xmlSheet)); // SP did not work try texture packer
                if (AnimationList == null) { return; }
                if (AnimationList.Count != 0)
                {
                    if (this.rtf_Console.InvokeRequired)
                    {
                        this.rtf_Console.Invoke((MethodInvoker)delegate
                        {
                            rtf_Console.AppendText("Attempting to load as Texture packer.....\n");
                        });

                    }
                    
                }
                else
                {
                    MessageBox.Show(
                         "Unknown XML file format",
                        "Error Unsupported format.",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return;
                }
                
            }
            frameListNames.Clear();
            frameListImages.Clear();
            CountFrames();
        }
    }
    #endregion Class
}
#endregion Namespace